Introduction

Flooding is the most common natural disaster for cities living near water. With increasing frequency and severity due to factors such as climate change and urbanization, there is a growing need for accurate flood inundation probability maps to help communities prepare and mitigate impacts.

The project aims to estimate a predictive flood inundation model that predicts the probability of an area being flooded. The model will be trained and validated using data from Calgary, Alberta (Canada) and used to predict flooding probability in Denver, a city with similar hydrologic and geographic features. This project will utilize both ArcGIS and R software to develop and analyze the flood inundation model. ArcGIS will be used to process and analyze spatial data, while R will be used for statistical analysis and machine learning algorithms. The resulting flood inundation probability maps will provide information of likelihood of flooding of Denver and provide a reference of flood inundation prediction model for other cities.

Setup

Loading libraries and jargons

To get started, we need to install and call the libraries we need. We will use a set of ggplot styles we call mapTheme and plotTheme.

library(plotROC)
library(tidyverse)
library(sf)
library(ggplot2)
library(spdep)
library(caTools)
library(plotROC)
library(caret)
library(pROC)
library(viridis)
library(gridExtra)
library(cowplot)
library(patchwork)
mapTheme <- theme(plot.title =element_text(size=12),
                  plot.subtitle = element_text(size=8),
                  plot.caption = element_text(size = 6),
                  axis.line=element_blank(),
                  axis.text.x=element_blank(),
                  axis.text.y=element_blank(),
                  axis.ticks=element_blank(),
                  axis.title.x=element_blank(),
                  axis.title.y=element_blank(),
                  panel.background=element_blank(),
                  panel.border=element_blank(),
                  panel.grid.major=element_line(colour = 'transparent'),
                  panel.grid.minor=element_blank(),
                  legend.direction = "vertical", 
                  legend.position = "right",
                  plot.margin = margin(1, 1, 1, 1, 'cm'),
                  legend.key.height = unit(1, "cm"), legend.key.width = unit(0.2, "cm"))

plotTheme <- theme(
  plot.title =element_text(size=12),
  plot.subtitle = element_text(size=8),
  plot.caption = element_text(size = 6),
  axis.text.x = element_text(size = 10, angle = 45, hjust = 1),
  axis.text.y = element_text(size = 10),
  axis.title.y = element_text(size = 10),
  # Set the entire chart region to blank
  panel.background=element_blank(),
  plot.background=element_blank(),
  #panel.border=element_rect(colour="#F0F0F0"),
  # Format the grid
  panel.grid.major=element_line(colour="#D0D0D0",size=.75),
  axis.ticks=element_blank())

Data wrangling

Data preprocessing

ArcGIS Pro was employed to generate predictive features such as slope, distance to streams with drainage greater than 50km2 and 100km2, flow accumulation, and imperious percentage of surface from terrain and impervious raster data. Furthermore, the predicted variable was obtained from Calgary open data, specifically the percentage area within the 10-year (10%) inundated zone, which was subsequently employed as the target variable. A similar process was employed in generating predictive features for Denver, with the exception that inundation data was not available

predictors <- c(
  "boundary", # ---------- 1: inside city boundary; 0: outside city boundary
  "dem", # --------------- From DEM; meters
  "slope", # ------------- Percentage rise
  "dist_big_streams", # -- Distance (meters) to streams with drainage > 50 km2
  "dist_huge_streams", # - Distance (meters) to streams with drainage > 100 km2
  "flow_accumulation", # - Flow accumulation (number of cells)
  "impervious" # --------- Impervious surface as percange of area
)

targets <- c(
  "inundation_1pct", # --- Percange area in 100-year (1%) inundated zone
  "inundation_10pct" # --- Percange area in 10-year (10%) inundated zone
)

The next step involved generating fishnet data for both cities, which was then used to aggregate the raster feature data. Zonal statistics were applied to compute the average values of raster features, with a 30 by 30-meter cell size. Next, a self-defined function was used to loop through and integrate the features into separate geo-dataframes for Calgary and Denver, respectively.

add_variables_from_csv <- function(city_fishnet, city_name, variable_list) {
  for (variable in variable_list) {
    path <- paste0(
      "~/Documents/GitHub/flood-inundation-map/data/fishnet-output/tbl_", city_name, "_", variable, ".csv"
    )
    
    data <- read.csv(path) %>%
      rename(id := OID_, !!variable := value)
    
    city_fishnet <- city_fishnet %>%
      left_join(data, by = "id")
  }
  
  city_fishnet <- city_fishnet %>%
    filter(boundary > 0.9) %>%
    dplyr::select(-boundary)
  
  return(city_fishnet)
}
calgary <- st_read("~/Documents/GitHub/flood-inundation-map/data/fishnet-output/calgary_fishnet.shp") %>%
  dplyr::select(id, geometry) %>%
  add_variables_from_csv("calgary", c(predictors, targets))

denver <- st_read("~/Documents/GitHub/flood-inundation-map/data/fishnet-output/denver_fishnet.shp") %>%
  dplyr::select(id, geometry) %>%
  add_variables_from_csv("denver", predictors)

Exploratory analysis

During the data exploration and manipulation stage, histograms were plotted to examine the distribution of the features. It was observed that the distributions of some features such as dist_big_streams, dist_huge_streams, and flow_accumulation were skewed, posing problems for analysis. To address this, a logarithmic transformation was applied to these features to normalize their distribution.

Below are the histograms of Calgary dataset.

## Function to plot histograms of the variables

data_df <- as.data.frame(calgary)

plot_histograms <- function(data, predictors,id_col = "id") {
  data_df <- as.data.frame(data)
  data_numeric <- data_df %>% dplyr::select(-!!sym(id_col)) %>% dplyr::select_if(is.numeric)
  
  # Initialize an empty list to store the plots
  plots <- list()
  
  for (predictor in predictors) {
    # Create a histogram for each predictor in the dataset
    p <- ggplot(data, aes_string(predictor)) +
      geom_histogram(fill = "grey") + # Removed binwidth parameter
      labs(title = paste("Histogram of", "\n",predictor),
           x = predictor,
           y = "Frequency") +
      theme_minimal()+
      theme(plot.title = element_text(size = 12))
      
    # Add the histograms to the list
    plots <- append(plots, list(p))
  }
  
  # Arrange the plots in a grid with 3 columns and 2 rows
  grid.arrange(grobs = plots, ncol = 3, nrow = 2)
}

# Call the function
plot_histograms(data=calgary, predictors = predictors2)

Below are the histograms of Denver dataset.

plot_histograms(denver, predictors2)

# Function to engineer features

# for calgary
engineer_features_calgary <- function(data) {
  data <- data %>%
    mutate(
      log_dist_big_streams = log(dist_big_streams),
      log_dist_huge_streams = log(dist_huge_streams),
      log_flow_accumulation = log(flow_accumulation),
      inundated = ifelse(inundation_10pct > 0.5, 1, 0)
    )
  return(data)
}

# for denver
engineer_features_denver <- function(data) {
  data <- data %>%
    mutate(
      log_dist_big_streams = log(dist_big_streams),
      log_dist_huge_streams = log(dist_huge_streams),
      log_flow_accumulation = log(flow_accumulation),
      #inundated = ifelse(inundation_1pct > 0.5, 1, 0)
    )
  return(data)
}

As for the target variable inundated, which was binarized from the original variable inundation_10pct, percentage area in 10-year (10%) inundated zone. The inundated variable was assigned a value using an ifelse statement. If the inundation_1pct variable was greater than 0.5, then the inundated variable was assigned a value of 1, which is positive in inundation. Otherwise, the inundated variable was assigned a value of 0.

# Get the predictors and target

predictors_used <- c(
  "dem", # --------------- From DEM; meters
  "slope", # ------------- Percentage rise
  "log_dist_big_streams",
  "log_dist_huge_streams",
  "log_flow_accumulation",
  "impervious" # --
)

lag_predictors_used <- predictors_used %>%
  sapply(., function(x) {
    paste0("lag_", x)
  }) %>%
  unname()


all_predictors_used <- c(predictors_used, lag_predictors_used)

target_used <- c("inundated")

calgary_used <- engineer_features_calgary(calgary) %>%
  dplyr::select(all_of(c("id", predictors_used, target_used)))

denver_used <- engineer_features_denver(denver) %>%
  dplyr::select(all_of(c("id", predictors_used)))

In the context of spatial analysis, it is important to account for spatial autocorrelation, which is the tendency for the values of a variable to be more similar at nearby locations than at distant ones. The calculate_spatial_lags function is used to address this issue by generating spatial lag variables, which are the weighted averages of the values of a variable in the neighboring locations. Incorporating spatial lags into our model helps capture the spatial patterns and dependencies within the data, leading to more accurate and reliable predictions.

# add predictor spatial lags

calculate_spatial_lags <-
  function(fishnet, predictors, id_col = "id", geometry_col = "geometry") {
    # Create neighbors list using the 'geometry' column
    nb <- poly2nb(fishnet, row.names = fishnet[[id_col]])
    
    # Create spatial weights matrix
    swm <- nb2listw(nb, style = "W", zero.policy = TRUE)
    
    # Calculate spatial lags for the specified predictor variables
    for (predictor in predictors) {
      spatial_lag_colname <- paste0("lag_", predictor)
      predictor_values <- as.numeric(fishnet[[predictor]])
      fishnet[[spatial_lag_colname]] <- lag.listw(swm, predictor_values, zero.policy = TRUE)
    }
    
    return(fishnet)
  }

calgary_used <- calgary_used %>%
  calculate_spatial_lags(predictors_used)

denver_used <- denver_used %>%
  calculate_spatial_lags(predictors_used)

Model building

Partition training and test sets

To build the model, relevant features and the target variable were selected, and then the data was randomly split into training (75%) and testing (25%) sets.

calgary_for_model <- calgary_used %>%
  dplyr::select(all_of(c(all_predictors_used, target_used)), id)

train_ratio <- 0.75
sample <- sample.split(calgary_for_model$inundated, SplitRatio = train_ratio)
train <- subset(calgary_for_model, sample == TRUE)
test <- subset(calgary_for_model, sample == FALSE)

Train a logistic model

The training set was used to train a logistic model using the glm() function, which specified the target variable inundatedand all other predictors. The predict function was then used to generate predicted probabilities for the test dataset, which was stored in the predicted_probs column.

model1 <- glm(
  inundated ~ ., train %>% dplyr::select(-id) %>% st_drop_geometry(),
  family = "binomial"(link = "logit")
)

test$predicted_probs <- predict(model1, test, type = "response")

Model summary

The summary output of the training logistic model provides the results of the logistic regression model. The summary shows which features are statistically significant in predicting the target variable in Calgary. In this case, the features dem, slope, log_flow_accumulation, lag_dem, lag_log_dist_big_streams, lag_log_dist_huge_streams, and lag_impervious were found to be statistically significant in predicting the target variable. This means that these features have a significant impact on the likelihood of an area being inundated.

summary(model1)
## 
## Call:
## glm(formula = inundated ~ ., family = binomial(link = "logit"), 
##     data = train %>% dplyr::select(-id) %>% st_drop_geometry())
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.3118  -0.0315  -0.0080  -0.0016   3.3563  
## 
## Coefficients:
##                           Estimate Std. Error z value Pr(>|z|)    
## (Intercept)               54.07350    7.63623   7.081 1.43e-12 ***
## dem                       -0.23006    0.06559  -3.508 0.000452 ***
## slope                     -0.10391    0.05762  -1.803 0.071335 .  
## log_dist_big_streams      -0.37555    1.71540  -0.219 0.826704    
## log_dist_huge_streams     -0.16387    1.72417  -0.095 0.924280    
## log_flow_accumulation      0.21305    0.06779   3.143 0.001674 ** 
## impervious                 1.45692    0.83760   1.739 0.081965 .  
## lag_dem                    0.17787    0.06521   2.728 0.006380 ** 
## lag_slope                 -0.04539    0.11510  -0.394 0.693352    
## lag_log_dist_big_streams   1.77704    2.45786   0.723 0.469677    
## lag_log_dist_huge_streams -2.67285    2.45601  -1.088 0.276466    
## lag_log_flow_accumulation  0.41361    0.15708   2.633 0.008461 ** 
## lag_impervious             2.40954    1.24950   1.928 0.053805 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 1145.71  on 6438  degrees of freedom
## Residual deviance:  445.31  on 6426  degrees of freedom
## AIC: 471.31
## 
## Number of Fisher Scoring iterations: 11

After identifying the statistically significant features, maps of those features were plot to visualize the spatial distribution of Calgary.

# Create individual plots
plot1 <- ggplot() +
  geom_sf(data=calgary_for_model, aes(fill=dem)) +
  scale_fill_viridis() +
  labs(title="DEM in meters") + mapTheme

plot2 <- ggplot() +
  geom_sf(data=calgary_for_model, aes(fill=slope)) +
  scale_fill_viridis() +
  labs(title="Slope,percentage rise") + mapTheme

combined_plot <- plot_grid(plot1, plot2,ncol=2) 
print(combined_plot)      

plot3 <- ggplot() +
  geom_sf(data=calgary_for_model, aes(fill=log_flow_accumulation)) +
  scale_fill_viridis(name="") +
  labs(title="Log flow accumulation") + 
  mapTheme

plot4 <- ggplot() +
  geom_sf(data=calgary_for_model, aes(fill=lag_log_dist_big_streams)) + 
  scale_fill_viridis(name="") +
  labs(title="Spatial lag log", "\nDistance (meters) to streams with drainage > 50 km2") +
  mapTheme

combined_plot <- plot_grid(plot3,plot4,ncol=2)
print(combined_plot)  

plot5 <- ggplot() +
  geom_sf(data=calgary_for_model, aes(fill=lag_log_dist_huge_streams)) + 
  scale_fill_viridis(name="") +
  labs(title="Spatial lag log", "\nDistance (meters) to streams with drainage > 100 km2") + mapTheme

plot6 <- ggplot() +
  geom_sf(data=calgary_for_model, aes(fill=lag_impervious)) + 
  scale_fill_viridis(name="") +
  labs(title="Spatial lag","\nImpervious surface as percange of area") + mapTheme

combined_plot <- plot_grid(plot5, plot6,ncol=2)
print(combined_plot)  

Model evaluation

Probability density plots

The summary output only gives the overall AIC model performance and statistically significant features. However, the summary could not evaluate the error and accuracy.

ggplot(test, aes(x = predicted_probs, fill = as.factor(inundated))) +
  geom_density() +
  facet_grid(inundated ~ ., scales = "free") +
  xlim(-0.1, 1) +
  labs(
    x = "Predicted Probability of Inundation",
    y = "Probability Density",
    title = "Distribution of predicted probabilities by observed outcome"
  )+
  scale_fill_manual(values = c("dark blue", "dark red"),
                    labels = c("Not Inundated","Inundated"),
                    name = "")+
  geom_vline(xintercept = .5)

To take a closer look, probability density plots were generated to show the distribution of predicted probabilities of inundation for the test set. The plots are arranged in a grid, with one plot for each value of the inundated variable. The blue plot represents areas that are not inundated, while the red plot represents areas that are inundated. The vertical line represents a 0.5 probability of inundation. If predicted probability is below 0.5, the predicted class is 0 (not inundated) and vice verses.

From the plot, we can observe that the False Negative rate is rather high. This indicates that there are cases where the actual class is 1 (inundated), but the model incorrectly assigns a predicted class of 0 (not inundated) due to the predicted probability being below 0.5. This suggests that the model may not be accurately identifying areas that are likely to be inundated.

Although the False Negative rate is high, the plot reveals a high True Positive rate. This means that the model is correctly predicting areas that are likely to be inundated, with a high probability. The high True Positive rate is encouraging, as it indicates that the model has some predictive power and can be useful in identifying areas that are vulnerable to flooding. However, it is still important to address the False Negative rate to improve the model’s accuracy and reliability.

ROC and AUC plots

The plot below shows the ROC curve and AUC for the model. The AUC value of 0.99 indicates that the model has a very high degree of accuracy in predicting whether an area will be inundated or not. However, it has potential to be overfitting with new datasets.

roc_data <- data.frame(
  D = as.logical(test$inundated),
  M = test$predicted_probs
)

ggplot(roc_data, aes(d = D, m = M)) +
  geom_roc() +
  geom_abline(slope = 1, intercept = 0, linewidth = 1.5, color = "grey") +
  labs(
    title = "ROC Curve",
    subtitle = paste("Area Under Curve (AUC):", round(auc(pROC::roc(test$inundated, test$predicted_probs)), 4)),
    x = "False Positive Rate (FPR)",
    y = "True Positive Rate (TPR)"
  ) 

Map prediction

Train on entire Calgary

To generated predicted inundation, the entire Calgray datasetcalgary_for_model was used to create a logistic regression model. where the target variable is inundated, and all other variables are predictors. After training the model, the predict function is used to generate predicted probabilities for the entire Calgary dataset. The predicted probabilities are stored in the calgary_for_model$predicted_probs column.

model2 <- glm(
  inundated ~ ., calgary_for_model %>% dplyr::select(-id) %>% st_drop_geometry(),
  family = "binomial"(link = "logit")
)

summary(model2)
## 
## Call:
## glm(formula = inundated ~ ., family = binomial(link = "logit"), 
##     data = calgary_for_model %>% dplyr::select(-id) %>% st_drop_geometry())
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.6114  -0.0325  -0.0092  -0.0021   3.2621  
## 
## Coefficients:
##                           Estimate Std. Error z value Pr(>|z|)    
## (Intercept)               49.06877    6.35052   7.727 1.10e-14 ***
## dem                       -0.26483    0.05540  -4.780 1.75e-06 ***
## slope                     -0.10228    0.04887  -2.093  0.03635 *  
## log_dist_big_streams      -0.05956    1.48488  -0.040  0.96800    
## log_dist_huge_streams     -0.06456    1.50139  -0.043  0.96570    
## log_flow_accumulation      0.25224    0.06038   4.177 2.95e-05 ***
## impervious                 2.31083    0.73405   3.148  0.00164 ** 
## lag_dem                    0.21849    0.05495   3.976 7.00e-05 ***
## lag_slope                 -0.07637    0.09697  -0.788  0.43096    
## lag_log_dist_big_streams   1.02312    2.12614   0.481  0.63037    
## lag_log_dist_huge_streams -2.38746    2.13018  -1.121  0.26238    
## lag_log_flow_accumulation  0.32242    0.13171   2.448  0.01437 *  
## lag_impervious             0.50835    1.05915   0.480  0.63125    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 1527.60  on 8584  degrees of freedom
## Residual deviance:  605.37  on 8572  degrees of freedom
## AIC: 631.37
## 
## Number of Fisher Scoring iterations: 11
calgary_for_model$predicted_probs <- predict(model2, calgary_for_model, type = "response")

Confusion matrix map for Calgary

To map the predicted inundation with error and accuracy, a confusion matrix map was plotted. The mutate function is used to create a new column called confResult, which categorizes the predicted and actual classes into four possible outcomes: True Negative, True Positive, False Negative, and False Positive.

calgary_for_model %>%
  mutate(confResult=case_when(predicted_probs < 0.5 & inundated==0 ~ "True_Negative",
                              predicted_probs >= 0.5 & inundated==1 ~ "True_Positive",
                              predicted_probs < 0.5 & inundated==1 ~ "False_Negative",
                              predicted_probs >= 0.5 & inundated==0 ~ "False_Positive")) %>%
  ggplot()+
  geom_sf(aes(fill = confResult), color = "transparent")+
  scale_fill_manual(values = c("Red","Orange","Light Blue","Light Green"),
                    name="Outcomes")+
  labs(title="Confusion Metrics") +
  mapTheme

Predicted inundation map for Calgary

Below is the predicted inundation map for Calgary trained using entire Calgary dataset.

ggplot() +
  geom_sf(data=calgary_for_model, aes(fill=predicted_probs)) + 
  scale_fill_gradient(low = "blue", high = "red", name = "Predicted probability") +
  labs(title="Predicted inundation") + mapTheme

Predicted inundation map Denver

Then the entire Calgary trained trained logistic regression model was used to predict inundation probabilities for the Denver dataset. The predict function is used to generate predicted probabilities for the Denver dataset using the model object created previously. The predicted probabilities are then stored in the predicted_probs column of the denver_used dataset and mapped using ggplot. This step allows us to apply the model to new data and evaluate how well it generalizes to other areas.

Though the predicted probabilities for Denver were comparatively small, the predicted class were distinguishable in

denver_used$predicted_probs <- predict(model2, denver_used, type = "response")

ggplot() +
  geom_sf(data=denver_used, aes(fill=predicted_probs)) + 
  scale_fill_gradient(low = "blue", high = "red",name = "Predicted probability") +
  labs(title="Predicted inundation") + mapTheme

Summary

This project aimed to estimate flood inundation probabilities using a predictive model for the cities of Calgary and Denver. Using logistic regression models, the prediction performance was evaluated using a confusion matrix and an ROC curve, which indicated an AUC of 0.99 and overall good performance.

However, the predicted probabilities for Denver were comparatively small, indicating a limitation in the model’s ability to generalize to new areas. Furthermore, the AUC is unusually high which might indicate potential overfitting problems. For future improvement, one possible consideration is to validate the model using additional cities, which could make the model more rigorous and accurate in predicting flood inundation probabilities for new locations.

LS0tCnRpdGxlOiAiRmxvb2QgSW51bmRhdGlvbiBNb2RlbGluZyIKYXV0aG9yOiAiWWluZ3RvbmdfWmhvbmciCmRhdGU6ICIyMDIzLTAzLTI4IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgSW50cm9kdWN0aW9uCkZsb29kaW5nIGlzIHRoZSBtb3N0IGNvbW1vbiBuYXR1cmFsIGRpc2FzdGVyIGZvciBjaXRpZXMgbGl2aW5nIG5lYXIgd2F0ZXIuIFdpdGggaW5jcmVhc2luZyBmcmVxdWVuY3kgYW5kIHNldmVyaXR5IGR1ZSB0byBmYWN0b3JzIHN1Y2ggYXMgY2xpbWF0ZSBjaGFuZ2UgYW5kIHVyYmFuaXphdGlvbiwgdGhlcmUgaXMgYSBncm93aW5nIG5lZWQgZm9yIGFjY3VyYXRlIGZsb29kIGludW5kYXRpb24gcHJvYmFiaWxpdHkgbWFwcyB0byBoZWxwIGNvbW11bml0aWVzIHByZXBhcmUgYW5kIG1pdGlnYXRlIGltcGFjdHMuCgpUaGUgcHJvamVjdCBhaW1zIHRvIGVzdGltYXRlIGEgcHJlZGljdGl2ZSBmbG9vZCBpbnVuZGF0aW9uIG1vZGVsIHRoYXQgcHJlZGljdHMgdGhlIHByb2JhYmlsaXR5IG9mIGFuIGFyZWEgYmVpbmcgZmxvb2RlZC4gVGhlIG1vZGVsIHdpbGwgYmUgdHJhaW5lZCBhbmQgdmFsaWRhdGVkIHVzaW5nIGRhdGEgZnJvbSBDYWxnYXJ5LCBBbGJlcnRhIChDYW5hZGEpIGFuZCB1c2VkIHRvIHByZWRpY3QgZmxvb2RpbmcgcHJvYmFiaWxpdHkgaW4gRGVudmVyLCBhIGNpdHkgd2l0aCBzaW1pbGFyIGh5ZHJvbG9naWMgYW5kIGdlb2dyYXBoaWMgZmVhdHVyZXMuIFRoaXMgcHJvamVjdCB3aWxsIHV0aWxpemUgYm90aCBBcmNHSVMgYW5kIFIgc29mdHdhcmUgdG8gZGV2ZWxvcCBhbmQgYW5hbHl6ZSB0aGUgZmxvb2QgaW51bmRhdGlvbiBtb2RlbC4gQXJjR0lTIHdpbGwgYmUgdXNlZCB0byBwcm9jZXNzIGFuZCBhbmFseXplIHNwYXRpYWwgZGF0YSwgd2hpbGUgUiB3aWxsIGJlIHVzZWQgZm9yIHN0YXRpc3RpY2FsIGFuYWx5c2lzIGFuZCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMuIFRoZSByZXN1bHRpbmcgZmxvb2QgaW51bmRhdGlvbiBwcm9iYWJpbGl0eSBtYXBzIHdpbGwgcHJvdmlkZSBpbmZvcm1hdGlvbiBvZiBsaWtlbGlob29kIG9mIGZsb29kaW5nIG9mIERlbnZlciBhbmQgcHJvdmlkZSBhIHJlZmVyZW5jZSBvZiBmbG9vZCBpbnVuZGF0aW9uIHByZWRpY3Rpb24gbW9kZWwgZm9yIG90aGVyIGNpdGllcy4KCgojIFNldHVwCgojIyBMb2FkaW5nIGxpYnJhcmllcyBhbmQgamFyZ29ucwpUbyBnZXQgc3RhcnRlZCwgd2UgbmVlZCB0byBpbnN0YWxsIGFuZCBjYWxsIHRoZSBsaWJyYXJpZXMgd2UgbmVlZC4gV2Ugd2lsbCB1c2UgYSBzZXQgb2YgZ2dwbG90IHN0eWxlcyB3ZSBjYWxsIGBtYXBUaGVtZWAgYW5kIGBwbG90VGhlbWVgLgoKCgpgYGB7ciBsaWJyYXJpZXMsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHBsb3RST0MpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc3BkZXApCmxpYnJhcnkoY2FUb29scykKbGlicmFyeShwbG90Uk9DKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwYXRjaHdvcmspCmBgYAoKYGBge3Igc2V0d2QsIGVjaG89RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChyb290LmRpciA9ICJ+L0RvY3VtZW50cy9HaXRIdWIvZmxvb2QtaW51bmRhdGlvbi1tYXAiKQpgYGAKCmBgYHtyIG1hcFRoZW1lLCB3YXJuaW5nID0gRkFMU0UsIGVjaG89VFJVRX0KbWFwVGhlbWUgPC0gdGhlbWUocGxvdC50aXRsZSA9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICAgICAgICAgIGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmJvcmRlcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9saW5lKGNvbG91ciA9ICd0cmFuc3BhcmVudCcpLAogICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsIAogICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxLCAxLCAxLCAxLCAnY20nKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDEsICJjbSIpLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjIsICJjbSIpKQoKcGxvdFRoZW1lIDwtIHRoZW1lKAogIHBsb3QudGl0bGUgPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAjIFNldCB0aGUgZW50aXJlIGNoYXJ0IHJlZ2lvbiB0byBibGFuawogIHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9ibGFuaygpLAogIHBsb3QuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksCiAgI3BhbmVsLmJvcmRlcj1lbGVtZW50X3JlY3QoY29sb3VyPSIjRjBGMEYwIiksCiAgIyBGb3JtYXQgdGhlIGdyaWQKICBwYW5lbC5ncmlkLm1ham9yPWVsZW1lbnRfbGluZShjb2xvdXI9IiNEMEQwRDAiLHNpemU9Ljc1KSwKICBheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIERhdGEgd3JhbmdsaW5nCiMjIERhdGEgcHJlcHJvY2Vzc2luZwoKQXJjR0lTIFBybyB3YXMgZW1wbG95ZWQgdG8gZ2VuZXJhdGUgcHJlZGljdGl2ZSBmZWF0dXJlcyBzdWNoIGFzIHNsb3BlLCBkaXN0YW5jZSB0byBzdHJlYW1zIHdpdGggZHJhaW5hZ2UgZ3JlYXRlciB0aGFuIDUwa20yIGFuZCAxMDBrbTIsIGZsb3cgYWNjdW11bGF0aW9uLCBhbmQgaW1wZXJpb3VzIHBlcmNlbnRhZ2Ugb2Ygc3VyZmFjZSBmcm9tIHRlcnJhaW4gYW5kIGltcGVydmlvdXMgcmFzdGVyIGRhdGEuIEZ1cnRoZXJtb3JlLCB0aGUgcHJlZGljdGVkIHZhcmlhYmxlIHdhcyBvYnRhaW5lZCBmcm9tIENhbGdhcnkgb3BlbiBkYXRhLCBzcGVjaWZpY2FsbHkgdGhlIHBlcmNlbnRhZ2UgYXJlYSB3aXRoaW4gdGhlIDEwLXllYXIgKDEwJSkgaW51bmRhdGVkIHpvbmUsIHdoaWNoIHdhcyBzdWJzZXF1ZW50bHkgZW1wbG95ZWQgYXMgdGhlIHRhcmdldCB2YXJpYWJsZS4gQSBzaW1pbGFyIHByb2Nlc3Mgd2FzIGVtcGxveWVkIGluIGdlbmVyYXRpbmcgcHJlZGljdGl2ZSBmZWF0dXJlcyBmb3IgRGVudmVyLCB3aXRoIHRoZSBleGNlcHRpb24gdGhhdCBpbnVuZGF0aW9uIGRhdGEgd2FzIG5vdCBhdmFpbGFibGUKCmBgYHtyIGZlYXR1cmVzIGFuZCB0YXJnZXQgdmFyaWFibGVzfQpwcmVkaWN0b3JzIDwtIGMoCiAgImJvdW5kYXJ5IiwgIyAtLS0tLS0tLS0tIDE6IGluc2lkZSBjaXR5IGJvdW5kYXJ5OyAwOiBvdXRzaWRlIGNpdHkgYm91bmRhcnkKICAiZGVtIiwgIyAtLS0tLS0tLS0tLS0tLS0gRnJvbSBERU07IG1ldGVycwogICJzbG9wZSIsICMgLS0tLS0tLS0tLS0tLSBQZXJjZW50YWdlIHJpc2UKICAiZGlzdF9iaWdfc3RyZWFtcyIsICMgLS0gRGlzdGFuY2UgKG1ldGVycykgdG8gc3RyZWFtcyB3aXRoIGRyYWluYWdlID4gNTAga20yCiAgImRpc3RfaHVnZV9zdHJlYW1zIiwgIyAtIERpc3RhbmNlIChtZXRlcnMpIHRvIHN0cmVhbXMgd2l0aCBkcmFpbmFnZSA+IDEwMCBrbTIKICAiZmxvd19hY2N1bXVsYXRpb24iLCAjIC0gRmxvdyBhY2N1bXVsYXRpb24gKG51bWJlciBvZiBjZWxscykKICAiaW1wZXJ2aW91cyIgIyAtLS0tLS0tLS0gSW1wZXJ2aW91cyBzdXJmYWNlIGFzIHBlcmNhbmdlIG9mIGFyZWEKKQoKdGFyZ2V0cyA8LSBjKAogICJpbnVuZGF0aW9uXzFwY3QiLCAjIC0tLSBQZXJjYW5nZSBhcmVhIGluIDEwMC15ZWFyICgxJSkgaW51bmRhdGVkIHpvbmUKICAiaW51bmRhdGlvbl8xMHBjdCIgIyAtLS0gUGVyY2FuZ2UgYXJlYSBpbiAxMC15ZWFyICgxMCUpIGludW5kYXRlZCB6b25lCikKYGBgCgpUaGUgbmV4dCBzdGVwIGludm9sdmVkIGdlbmVyYXRpbmcgZmlzaG5ldCBkYXRhIGZvciBib3RoIGNpdGllcywgd2hpY2ggd2FzIHRoZW4gdXNlZCB0byBhZ2dyZWdhdGUgdGhlIHJhc3RlciBmZWF0dXJlIGRhdGEuIFpvbmFsIHN0YXRpc3RpY3Mgd2VyZSBhcHBsaWVkIHRvIGNvbXB1dGUgdGhlIGF2ZXJhZ2UgdmFsdWVzIG9mIHJhc3RlciBmZWF0dXJlcywgd2l0aCBhIDMwIGJ5IDMwLW1ldGVyIGNlbGwgc2l6ZS4gTmV4dCwgYSBzZWxmLWRlZmluZWQgZnVuY3Rpb24gd2FzIHVzZWQgdG8gbG9vcCB0aHJvdWdoIGFuZCBpbnRlZ3JhdGUgdGhlIGZlYXR1cmVzIGludG8gc2VwYXJhdGUgZ2VvLWRhdGFmcmFtZXMgZm9yIENhbGdhcnkgYW5kIERlbnZlciwgcmVzcGVjdGl2ZWx5LgoKYGBge3IgYWRkX2RhdGEsIG1lc3NhZ2U9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmFkZF92YXJpYWJsZXNfZnJvbV9jc3YgPC0gZnVuY3Rpb24oY2l0eV9maXNobmV0LCBjaXR5X25hbWUsIHZhcmlhYmxlX2xpc3QpIHsKICBmb3IgKHZhcmlhYmxlIGluIHZhcmlhYmxlX2xpc3QpIHsKICAgIHBhdGggPC0gcGFzdGUwKAogICAgICAifi9Eb2N1bWVudHMvR2l0SHViL2Zsb29kLWludW5kYXRpb24tbWFwL2RhdGEvZmlzaG5ldC1vdXRwdXQvdGJsXyIsIGNpdHlfbmFtZSwgIl8iLCB2YXJpYWJsZSwgIi5jc3YiCiAgICApCiAgICAKICAgIGRhdGEgPC0gcmVhZC5jc3YocGF0aCkgJT4lCiAgICAgIHJlbmFtZShpZCA6PSBPSURfLCAhIXZhcmlhYmxlIDo9IHZhbHVlKQogICAgCiAgICBjaXR5X2Zpc2huZXQgPC0gY2l0eV9maXNobmV0ICU+JQogICAgICBsZWZ0X2pvaW4oZGF0YSwgYnkgPSAiaWQiKQogIH0KICAKICBjaXR5X2Zpc2huZXQgPC0gY2l0eV9maXNobmV0ICU+JQogICAgZmlsdGVyKGJvdW5kYXJ5ID4gMC45KSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoLWJvdW5kYXJ5KQogIAogIHJldHVybihjaXR5X2Zpc2huZXQpCn0KYGBgCgpgYGB7ciByZWFkX2RhdGEsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLHJlc3VsdHMgPSAiaGlkZSJ9CmNhbGdhcnkgPC0gc3RfcmVhZCgifi9Eb2N1bWVudHMvR2l0SHViL2Zsb29kLWludW5kYXRpb24tbWFwL2RhdGEvZmlzaG5ldC1vdXRwdXQvY2FsZ2FyeV9maXNobmV0LnNocCIpICU+JQogIGRwbHlyOjpzZWxlY3QoaWQsIGdlb21ldHJ5KSAlPiUKICBhZGRfdmFyaWFibGVzX2Zyb21fY3N2KCJjYWxnYXJ5IiwgYyhwcmVkaWN0b3JzLCB0YXJnZXRzKSkKCmRlbnZlciA8LSBzdF9yZWFkKCJ+L0RvY3VtZW50cy9HaXRIdWIvZmxvb2QtaW51bmRhdGlvbi1tYXAvZGF0YS9maXNobmV0LW91dHB1dC9kZW52ZXJfZmlzaG5ldC5zaHAiKSAlPiUKICBkcGx5cjo6c2VsZWN0KGlkLCBnZW9tZXRyeSkgJT4lCiAgYWRkX3ZhcmlhYmxlc19mcm9tX2NzdigiZGVudmVyIiwgcHJlZGljdG9ycykKYGBgCgojIyBFeHBsb3JhdG9yeSBhbmFseXNpcwoKRHVyaW5nIHRoZSBkYXRhIGV4cGxvcmF0aW9uIGFuZCBtYW5pcHVsYXRpb24gc3RhZ2UsIGhpc3RvZ3JhbXMgd2VyZSBwbG90dGVkIHRvIGV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZmVhdHVyZXMuIEl0IHdhcyBvYnNlcnZlZCB0aGF0IHRoZSBkaXN0cmlidXRpb25zIG9mIHNvbWUgZmVhdHVyZXMgc3VjaCBhcyBgZGlzdF9iaWdfc3RyZWFtc2AsIGBkaXN0X2h1Z2Vfc3RyZWFtc2AsIGFuZCBgZmxvd19hY2N1bXVsYXRpb25gIHdlcmUgc2tld2VkLCBwb3NpbmcgcHJvYmxlbXMgZm9yIGFuYWx5c2lzLiBUbyBhZGRyZXNzIHRoaXMsIGEgbG9nYXJpdGhtaWMgdHJhbnNmb3JtYXRpb24gd2FzIGFwcGxpZWQgdG8gdGhlc2UgZmVhdHVyZXMgdG8gbm9ybWFsaXplIHRoZWlyIGRpc3RyaWJ1dGlvbi4KCmBgYHtyIHByZWRpY3RvcnNfdXBkYXRlLCBlY2hvPUZBTFNFfQpwcmVkaWN0b3JzMiA8LSBjKAogICJkZW0iLCAjIC0tLS0tLS0tLS0tLS0tLSBGcm9tIERFTTsgbWV0ZXJzCiAgInNsb3BlIiwgIyAtLS0tLS0tLS0tLS0tIFBlcmNlbnRhZ2UgcmlzZQogICJkaXN0X2JpZ19zdHJlYW1zIiwgIyAtLSBEaXN0YW5jZSAobWV0ZXJzKSB0byBzdHJlYW1zIHdpdGggZHJhaW5hZ2UgPiA1MCBrbTIKICAiZGlzdF9odWdlX3N0cmVhbXMiLCAjIC0gRGlzdGFuY2UgKG1ldGVycykgdG8gc3RyZWFtcyB3aXRoIGRyYWluYWdlID4gMTAwIGttMgogICJmbG93X2FjY3VtdWxhdGlvbiIsICMgLSBGbG93IGFjY3VtdWxhdGlvbiAobnVtYmVyIG9mIGNlbGxzKQogICJpbXBlcnZpb3VzIiAjIC0tLS0tLS0tLSBJbXBlcnZpb3VzIHN1cmZhY2UgYXMgcGVyY2FuZ2Ugb2YgYXJlYQopCmBgYAoKQmVsb3cgYXJlIHRoZSBoaXN0b2dyYW1zIG9mIGBDYWxnYXJ5YCBkYXRhc2V0LgpgYGB7ciBwbG90X2hpc3QsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIyBGdW5jdGlvbiB0byBwbG90IGhpc3RvZ3JhbXMgb2YgdGhlIHZhcmlhYmxlcwoKZGF0YV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNhbGdhcnkpCgpwbG90X2hpc3RvZ3JhbXMgPC0gZnVuY3Rpb24oZGF0YSwgcHJlZGljdG9ycyxpZF9jb2wgPSAiaWQiKSB7CiAgZGF0YV9kZiA8LSBhcy5kYXRhLmZyYW1lKGRhdGEpCiAgZGF0YV9udW1lcmljIDwtIGRhdGFfZGYgJT4lIGRwbHlyOjpzZWxlY3QoLSEhc3ltKGlkX2NvbCkpICU+JSBkcGx5cjo6c2VsZWN0X2lmKGlzLm51bWVyaWMpCiAgCiAgIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIHBsb3RzCiAgcGxvdHMgPC0gbGlzdCgpCiAgCiAgZm9yIChwcmVkaWN0b3IgaW4gcHJlZGljdG9ycykgewogICAgIyBDcmVhdGUgYSBoaXN0b2dyYW0gZm9yIGVhY2ggcHJlZGljdG9yIGluIHRoZSBkYXRhc2V0CiAgICBwIDwtIGdncGxvdChkYXRhLCBhZXNfc3RyaW5nKHByZWRpY3RvcikpICsKICAgICAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJncmV5IikgKyAjIFJlbW92ZWQgYmlud2lkdGggcGFyYW1ldGVyCiAgICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiSGlzdG9ncmFtIG9mIiwgIlxuIixwcmVkaWN0b3IpLAogICAgICAgICAgIHggPSBwcmVkaWN0b3IsCiAgICAgICAgICAgeSA9ICJGcmVxdWVuY3kiKSArCiAgICAgIHRoZW1lX21pbmltYWwoKSsKICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKQogICAgICAKICAgICMgQWRkIHRoZSBoaXN0b2dyYW1zIHRvIHRoZSBsaXN0CiAgICBwbG90cyA8LSBhcHBlbmQocGxvdHMsIGxpc3QocCkpCiAgfQogIAogICMgQXJyYW5nZSB0aGUgcGxvdHMgaW4gYSBncmlkIHdpdGggMyBjb2x1bW5zIGFuZCAyIHJvd3MKICBncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbG90cywgbmNvbCA9IDMsIG5yb3cgPSAyKQp9CgojIENhbGwgdGhlIGZ1bmN0aW9uCnBsb3RfaGlzdG9ncmFtcyhkYXRhPWNhbGdhcnksIHByZWRpY3RvcnMgPSBwcmVkaWN0b3JzMikKYGBgCgpCZWxvdyBhcmUgdGhlIGhpc3RvZ3JhbXMgb2YgYERlbnZlcmAgZGF0YXNldC4KYGBge3IgcGxvdF9oaXN0Miwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnBsb3RfaGlzdG9ncmFtcyhkZW52ZXIsIHByZWRpY3RvcnMyKQpgYGAKCmBgYHtyIGVuZ2luZWVyX2ZlYXR1cmVzLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyBGdW5jdGlvbiB0byBlbmdpbmVlciBmZWF0dXJlcwoKIyBmb3IgY2FsZ2FyeQplbmdpbmVlcl9mZWF0dXJlc19jYWxnYXJ5IDwtIGZ1bmN0aW9uKGRhdGEpIHsKICBkYXRhIDwtIGRhdGEgJT4lCiAgICBtdXRhdGUoCiAgICAgIGxvZ19kaXN0X2JpZ19zdHJlYW1zID0gbG9nKGRpc3RfYmlnX3N0cmVhbXMpLAogICAgICBsb2dfZGlzdF9odWdlX3N0cmVhbXMgPSBsb2coZGlzdF9odWdlX3N0cmVhbXMpLAogICAgICBsb2dfZmxvd19hY2N1bXVsYXRpb24gPSBsb2coZmxvd19hY2N1bXVsYXRpb24pLAogICAgICBpbnVuZGF0ZWQgPSBpZmVsc2UoaW51bmRhdGlvbl8xMHBjdCA+IDAuNSwgMSwgMCkKICAgICkKICByZXR1cm4oZGF0YSkKfQoKIyBmb3IgZGVudmVyCmVuZ2luZWVyX2ZlYXR1cmVzX2RlbnZlciA8LSBmdW5jdGlvbihkYXRhKSB7CiAgZGF0YSA8LSBkYXRhICU+JQogICAgbXV0YXRlKAogICAgICBsb2dfZGlzdF9iaWdfc3RyZWFtcyA9IGxvZyhkaXN0X2JpZ19zdHJlYW1zKSwKICAgICAgbG9nX2Rpc3RfaHVnZV9zdHJlYW1zID0gbG9nKGRpc3RfaHVnZV9zdHJlYW1zKSwKICAgICAgbG9nX2Zsb3dfYWNjdW11bGF0aW9uID0gbG9nKGZsb3dfYWNjdW11bGF0aW9uKSwKICAgICAgI2ludW5kYXRlZCA9IGlmZWxzZShpbnVuZGF0aW9uXzFwY3QgPiAwLjUsIDEsIDApCiAgICApCiAgcmV0dXJuKGRhdGEpCn0KYGBgCgpBcyBmb3IgdGhlIHRhcmdldCB2YXJpYWJsZSBgaW51bmRhdGVkYCwgd2hpY2ggd2FzIGJpbmFyaXplZCBmcm9tIHRoZSBvcmlnaW5hbCB2YXJpYWJsZSBgaW51bmRhdGlvbl8xMHBjdGAsIHBlcmNlbnRhZ2UgYXJlYSBpbiAxMC15ZWFyICgxMCUpIGludW5kYXRlZCB6b25lLiAgVGhlIGludW5kYXRlZCB2YXJpYWJsZSB3YXMgYXNzaWduZWQgYSB2YWx1ZSB1c2luZyBhbiBpZmVsc2Ugc3RhdGVtZW50LiBJZiB0aGUgaW51bmRhdGlvbl8xcGN0IHZhcmlhYmxlIHdhcyBncmVhdGVyIHRoYW4gMC41LCB0aGVuIHRoZSBpbnVuZGF0ZWQgdmFyaWFibGUgd2FzIGFzc2lnbmVkIGEgdmFsdWUgb2YgYDFgLCB3aGljaCBpcyBwb3NpdGl2ZSBpbiBpbnVuZGF0aW9uLiBPdGhlcndpc2UsIHRoZSBpbnVuZGF0ZWQgdmFyaWFibGUgd2FzIGFzc2lnbmVkIGEgdmFsdWUgb2YgYDBgLgoKYGBge3IgZGF0YSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgR2V0IHRoZSBwcmVkaWN0b3JzIGFuZCB0YXJnZXQKCnByZWRpY3RvcnNfdXNlZCA8LSBjKAogICJkZW0iLCAjIC0tLS0tLS0tLS0tLS0tLSBGcm9tIERFTTsgbWV0ZXJzCiAgInNsb3BlIiwgIyAtLS0tLS0tLS0tLS0tIFBlcmNlbnRhZ2UgcmlzZQogICJsb2dfZGlzdF9iaWdfc3RyZWFtcyIsCiAgImxvZ19kaXN0X2h1Z2Vfc3RyZWFtcyIsCiAgImxvZ19mbG93X2FjY3VtdWxhdGlvbiIsCiAgImltcGVydmlvdXMiICMgLS0KKQoKbGFnX3ByZWRpY3RvcnNfdXNlZCA8LSBwcmVkaWN0b3JzX3VzZWQgJT4lCiAgc2FwcGx5KC4sIGZ1bmN0aW9uKHgpIHsKICAgIHBhc3RlMCgibGFnXyIsIHgpCiAgfSkgJT4lCiAgdW5uYW1lKCkKCgphbGxfcHJlZGljdG9yc191c2VkIDwtIGMocHJlZGljdG9yc191c2VkLCBsYWdfcHJlZGljdG9yc191c2VkKQoKdGFyZ2V0X3VzZWQgPC0gYygiaW51bmRhdGVkIikKCmNhbGdhcnlfdXNlZCA8LSBlbmdpbmVlcl9mZWF0dXJlc19jYWxnYXJ5KGNhbGdhcnkpICU+JQogIGRwbHlyOjpzZWxlY3QoYWxsX29mKGMoImlkIiwgcHJlZGljdG9yc191c2VkLCB0YXJnZXRfdXNlZCkpKQoKZGVudmVyX3VzZWQgPC0gZW5naW5lZXJfZmVhdHVyZXNfZGVudmVyKGRlbnZlcikgJT4lCiAgZHBseXI6OnNlbGVjdChhbGxfb2YoYygiaWQiLCBwcmVkaWN0b3JzX3VzZWQpKSkKYGBgCgpJbiB0aGUgY29udGV4dCBvZiBzcGF0aWFsIGFuYWx5c2lzLCBpdCBpcyBpbXBvcnRhbnQgdG8gYWNjb3VudCBmb3Igc3BhdGlhbCBhdXRvY29ycmVsYXRpb24sIHdoaWNoIGlzIHRoZSB0ZW5kZW5jeSBmb3IgdGhlIHZhbHVlcyBvZiBhIHZhcmlhYmxlIHRvIGJlIG1vcmUgc2ltaWxhciBhdCBuZWFyYnkgbG9jYXRpb25zIHRoYW4gYXQgZGlzdGFudCBvbmVzLiBUaGUgYGNhbGN1bGF0ZV9zcGF0aWFsX2xhZ3NgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gYWRkcmVzcyB0aGlzIGlzc3VlIGJ5IGdlbmVyYXRpbmcgc3BhdGlhbCBsYWcgdmFyaWFibGVzLCB3aGljaCBhcmUgdGhlIHdlaWdodGVkIGF2ZXJhZ2VzIG9mIHRoZSB2YWx1ZXMgb2YgYSB2YXJpYWJsZSBpbiB0aGUgbmVpZ2hib3JpbmcgbG9jYXRpb25zLiBJbmNvcnBvcmF0aW5nIHNwYXRpYWwgbGFncyBpbnRvIG91ciBtb2RlbCBoZWxwcyBjYXB0dXJlIHRoZSBzcGF0aWFsIHBhdHRlcm5zIGFuZCBkZXBlbmRlbmNpZXMgd2l0aGluIHRoZSBkYXRhLCBsZWFkaW5nIHRvIG1vcmUgYWNjdXJhdGUgYW5kIHJlbGlhYmxlIHByZWRpY3Rpb25zLgoKCmBgYHtyIHNwYXRpYWxfbGFncywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgYWRkIHByZWRpY3RvciBzcGF0aWFsIGxhZ3MKCmNhbGN1bGF0ZV9zcGF0aWFsX2xhZ3MgPC0KICBmdW5jdGlvbihmaXNobmV0LCBwcmVkaWN0b3JzLCBpZF9jb2wgPSAiaWQiLCBnZW9tZXRyeV9jb2wgPSAiZ2VvbWV0cnkiKSB7CiAgICAjIENyZWF0ZSBuZWlnaGJvcnMgbGlzdCB1c2luZyB0aGUgJ2dlb21ldHJ5JyBjb2x1bW4KICAgIG5iIDwtIHBvbHkybmIoZmlzaG5ldCwgcm93Lm5hbWVzID0gZmlzaG5ldFtbaWRfY29sXV0pCiAgICAKICAgICMgQ3JlYXRlIHNwYXRpYWwgd2VpZ2h0cyBtYXRyaXgKICAgIHN3bSA8LSBuYjJsaXN0dyhuYiwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkKICAgIAogICAgIyBDYWxjdWxhdGUgc3BhdGlhbCBsYWdzIGZvciB0aGUgc3BlY2lmaWVkIHByZWRpY3RvciB2YXJpYWJsZXMKICAgIGZvciAocHJlZGljdG9yIGluIHByZWRpY3RvcnMpIHsKICAgICAgc3BhdGlhbF9sYWdfY29sbmFtZSA8LSBwYXN0ZTAoImxhZ18iLCBwcmVkaWN0b3IpCiAgICAgIHByZWRpY3Rvcl92YWx1ZXMgPC0gYXMubnVtZXJpYyhmaXNobmV0W1twcmVkaWN0b3JdXSkKICAgICAgZmlzaG5ldFtbc3BhdGlhbF9sYWdfY29sbmFtZV1dIDwtIGxhZy5saXN0dyhzd20sIHByZWRpY3Rvcl92YWx1ZXMsIHplcm8ucG9saWN5ID0gVFJVRSkKICAgIH0KICAgIAogICAgcmV0dXJuKGZpc2huZXQpCiAgfQoKY2FsZ2FyeV91c2VkIDwtIGNhbGdhcnlfdXNlZCAlPiUKICBjYWxjdWxhdGVfc3BhdGlhbF9sYWdzKHByZWRpY3RvcnNfdXNlZCkKCmRlbnZlcl91c2VkIDwtIGRlbnZlcl91c2VkICU+JQogIGNhbGN1bGF0ZV9zcGF0aWFsX2xhZ3MocHJlZGljdG9yc191c2VkKQpgYGAKCiMgTW9kZWwgYnVpbGRpbmcKCiMjIFBhcnRpdGlvbiB0cmFpbmluZyBhbmQgdGVzdCBzZXRzIAoKVG8gYnVpbGQgdGhlIG1vZGVsLCByZWxldmFudCBmZWF0dXJlcyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZSB3ZXJlIHNlbGVjdGVkLCBhbmQgdGhlbiB0aGUgZGF0YSB3YXMgcmFuZG9tbHkgc3BsaXQgaW50byB0cmFpbmluZyAoNzUlKSBhbmQgdGVzdGluZyAoMjUlKSBzZXRzLiAKCmBgYHtyIHRyYWluX3Rlc3Rfc3BsaXQsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpjYWxnYXJ5X2Zvcl9tb2RlbCA8LSBjYWxnYXJ5X3VzZWQgJT4lCiAgZHBseXI6OnNlbGVjdChhbGxfb2YoYyhhbGxfcHJlZGljdG9yc191c2VkLCB0YXJnZXRfdXNlZCkpLCBpZCkKCnRyYWluX3JhdGlvIDwtIDAuNzUKc2FtcGxlIDwtIHNhbXBsZS5zcGxpdChjYWxnYXJ5X2Zvcl9tb2RlbCRpbnVuZGF0ZWQsIFNwbGl0UmF0aW8gPSB0cmFpbl9yYXRpbykKdHJhaW4gPC0gc3Vic2V0KGNhbGdhcnlfZm9yX21vZGVsLCBzYW1wbGUgPT0gVFJVRSkKdGVzdCA8LSBzdWJzZXQoY2FsZ2FyeV9mb3JfbW9kZWwsIHNhbXBsZSA9PSBGQUxTRSkKYGBgCgoKIyMgVHJhaW4gYSBsb2dpc3RpYyBtb2RlbAoKVGhlIHRyYWluaW5nIHNldCB3YXMgdXNlZCB0byB0cmFpbiBhIGxvZ2lzdGljIG1vZGVsIHVzaW5nIHRoZSBgZ2xtKClgIGZ1bmN0aW9uLCB3aGljaCBzcGVjaWZpZWQgdGhlIHRhcmdldCB2YXJpYWJsZSBgIGludW5kYXRlZCBgYW5kIGFsbCBvdGhlciBwcmVkaWN0b3JzLiBUaGUgcHJlZGljdCBmdW5jdGlvbiB3YXMgdGhlbiB1c2VkIHRvIGdlbmVyYXRlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGZvciB0aGUgYHRlc3RgIGRhdGFzZXQsIHdoaWNoIHdhcyBzdG9yZWQgaW4gdGhlIGBwcmVkaWN0ZWRfcHJvYnNgIGNvbHVtbi4KCgpgYGB7ciB0cmFpbl9tb2RlbCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9Cm1vZGVsMSA8LSBnbG0oCiAgaW51bmRhdGVkIH4gLiwgdHJhaW4gJT4lIGRwbHlyOjpzZWxlY3QoLWlkKSAlPiUgc3RfZHJvcF9nZW9tZXRyeSgpLAogIGZhbWlseSA9ICJiaW5vbWlhbCIobGluayA9ICJsb2dpdCIpCikKCnRlc3QkcHJlZGljdGVkX3Byb2JzIDwtIHByZWRpY3QobW9kZWwxLCB0ZXN0LCB0eXBlID0gInJlc3BvbnNlIikKYGBgCgojIyBNb2RlbCBzdW1tYXJ5ClRoZSBzdW1tYXJ5IG91dHB1dCBvZiB0aGUgdHJhaW5pbmcgbG9naXN0aWMgbW9kZWwgcHJvdmlkZXMgdGhlIHJlc3VsdHMgb2YgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuIFRoZSBzdW1tYXJ5IHNob3dzIHdoaWNoIGZlYXR1cmVzIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGluIHByZWRpY3RpbmcgdGhlIHRhcmdldCB2YXJpYWJsZSBpbiBDYWxnYXJ5LiBJbiB0aGlzIGNhc2UsIHRoZSBmZWF0dXJlcyBgZGVtYCwgYHNsb3BlYCwgYGxvZ19mbG93X2FjY3VtdWxhdGlvbmAsIGBsYWdfZGVtYCwgYGxhZ19sb2dfZGlzdF9iaWdfc3RyZWFtc2AsIGBsYWdfbG9nX2Rpc3RfaHVnZV9zdHJlYW1zYCwgYW5kIGBsYWdfaW1wZXJ2aW91c2Agd2VyZSBmb3VuZCB0byBiZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGluIHByZWRpY3RpbmcgdGhlIHRhcmdldCB2YXJpYWJsZS4gVGhpcyBtZWFucyB0aGF0IHRoZXNlIGZlYXR1cmVzIGhhdmUgYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gdGhlIGxpa2VsaWhvb2Qgb2YgYW4gYXJlYSBiZWluZyBpbnVuZGF0ZWQuCgpgYGB7ciBNb2RlbF9zdW1tYXJ5LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kc3VtbWFyeShtb2RlbDEpCmBgYAoKQWZ0ZXIgaWRlbnRpZnlpbmcgdGhlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZmVhdHVyZXMsIG1hcHMgb2YgdGhvc2UgZmVhdHVyZXMgd2VyZSBwbG90IHRvIHZpc3VhbGl6ZSB0aGUgc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgQ2FsZ2FyeS4KCmBgYHtyIEZlYXR1cmVfbWFwcGluZzEsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIENyZWF0ZSBpbmRpdmlkdWFsIHBsb3RzCnBsb3QxIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Y2FsZ2FyeV9mb3JfbW9kZWwsIGFlcyhmaWxsPWRlbSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoKSArCiAgbGFicyh0aXRsZT0iREVNIGluIG1ldGVycyIpICsgbWFwVGhlbWUKCnBsb3QyIDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Y2FsZ2FyeV9mb3JfbW9kZWwsIGFlcyhmaWxsPXNsb3BlKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcygpICsKICBsYWJzKHRpdGxlPSJTbG9wZSxwZXJjZW50YWdlIHJpc2UiKSArIG1hcFRoZW1lCgpjb21iaW5lZF9wbG90IDwtIHBsb3RfZ3JpZChwbG90MSwgcGxvdDIsbmNvbD0yKSAKcHJpbnQoY29tYmluZWRfcGxvdCkgICAgICAKYGBgCmBgYHtyIEZlYXR1cmVfbWFwcGluZzIsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKcGxvdDMgPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1jYWxnYXJ5X2Zvcl9tb2RlbCwgYWVzKGZpbGw9bG9nX2Zsb3dfYWNjdW11bGF0aW9uKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iTG9nIGZsb3cgYWNjdW11bGF0aW9uIikgKyAKICBtYXBUaGVtZQoKcGxvdDQgPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1jYWxnYXJ5X2Zvcl9tb2RlbCwgYWVzKGZpbGw9bGFnX2xvZ19kaXN0X2JpZ19zdHJlYW1zKSkgKyAKICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZT0iIikgKwogIGxhYnModGl0bGU9IlNwYXRpYWwgbGFnIGxvZyIsICJcbkRpc3RhbmNlIChtZXRlcnMpIHRvIHN0cmVhbXMgd2l0aCBkcmFpbmFnZSA+IDUwIGttMiIpICsKICBtYXBUaGVtZQoKY29tYmluZWRfcGxvdCA8LSBwbG90X2dyaWQocGxvdDMscGxvdDQsbmNvbD0yKQpwcmludChjb21iaW5lZF9wbG90KSAgCmBgYAoKYGBge3IgRmVhdHVyZV9tYXBwaW5nMywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnBsb3Q1IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Y2FsZ2FyeV9mb3JfbW9kZWwsIGFlcyhmaWxsPWxhZ19sb2dfZGlzdF9odWdlX3N0cmVhbXMpKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpcyhuYW1lPSIiKSArCiAgbGFicyh0aXRsZT0iU3BhdGlhbCBsYWcgbG9nIiwgIlxuRGlzdGFuY2UgKG1ldGVycykgdG8gc3RyZWFtcyB3aXRoIGRyYWluYWdlID4gMTAwIGttMiIpICsgbWFwVGhlbWUKCnBsb3Q2IDwtIGdncGxvdCgpICsKICBnZW9tX3NmKGRhdGE9Y2FsZ2FyeV9mb3JfbW9kZWwsIGFlcyhmaWxsPWxhZ19pbXBlcnZpb3VzKSkgKyAKICBzY2FsZV9maWxsX3ZpcmlkaXMobmFtZT0iIikgKwogIGxhYnModGl0bGU9IlNwYXRpYWwgbGFnIiwiXG5JbXBlcnZpb3VzIHN1cmZhY2UgYXMgcGVyY2FuZ2Ugb2YgYXJlYSIpICsgbWFwVGhlbWUKCmNvbWJpbmVkX3Bsb3QgPC0gcGxvdF9ncmlkKHBsb3Q1LCBwbG90NixuY29sPTIpCnByaW50KGNvbWJpbmVkX3Bsb3QpICAKYGBgCgoKIyBNb2RlbCBldmFsdWF0aW9uCgojIyBQcm9iYWJpbGl0eSBkZW5zaXR5IHBsb3RzCgpUaGUgc3VtbWFyeSBvdXRwdXQgb25seSBnaXZlcyB0aGUgb3ZlcmFsbCBBSUMgbW9kZWwgcGVyZm9ybWFuY2UgYW5kIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZmVhdHVyZXMuIEhvd2V2ZXIsIHRoZSBzdW1tYXJ5IGNvdWxkIG5vdCBldmFsdWF0ZSB0aGUgZXJyb3IgYW5kIGFjY3VyYWN5LiAKCmBgYHtyIFByb2JhYmlsaXR5X3Bsb3QsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpnZ3Bsb3QodGVzdCwgYWVzKHggPSBwcmVkaWN0ZWRfcHJvYnMsIGZpbGwgPSBhcy5mYWN0b3IoaW51bmRhdGVkKSkpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgZmFjZXRfZ3JpZChpbnVuZGF0ZWQgfiAuLCBzY2FsZXMgPSAiZnJlZSIpICsKICB4bGltKC0wLjEsIDEpICsKICBsYWJzKAogICAgeCA9ICJQcmVkaWN0ZWQgUHJvYmFiaWxpdHkgb2YgSW51bmRhdGlvbiIsCiAgICB5ID0gIlByb2JhYmlsaXR5IERlbnNpdHkiLAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGJ5IG9ic2VydmVkIG91dGNvbWUiCiAgKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJkYXJrIGJsdWUiLCAiZGFyayByZWQiKSwKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJOb3QgSW51bmRhdGVkIiwiSW51bmRhdGVkIiksCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICIiKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAuNSkKYGBgCgpUbyB0YWtlIGEgY2xvc2VyIGxvb2ssIHByb2JhYmlsaXR5IGRlbnNpdHkgcGxvdHMgd2VyZSBnZW5lcmF0ZWQgdG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIG9mIGludW5kYXRpb24gZm9yIHRoZSB0ZXN0IHNldC4gVGhlIHBsb3RzIGFyZSBhcnJhbmdlZCBpbiBhIGdyaWQsIHdpdGggb25lIHBsb3QgZm9yIGVhY2ggdmFsdWUgb2YgdGhlIGludW5kYXRlZCB2YXJpYWJsZS4gVGhlIGJsdWUgcGxvdCByZXByZXNlbnRzIGFyZWFzIHRoYXQgYXJlIG5vdCBpbnVuZGF0ZWQsIHdoaWxlIHRoZSByZWQgcGxvdCByZXByZXNlbnRzIGFyZWFzIHRoYXQgYXJlIGludW5kYXRlZC4gVGhlIHZlcnRpY2FsIGxpbmUgcmVwcmVzZW50cyBhIDAuNSBwcm9iYWJpbGl0eSBvZiBpbnVuZGF0aW9uLiBJZiBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgaXMgYmVsb3cgMC41LCB0aGUgcHJlZGljdGVkIGNsYXNzIGlzIGAwYCAobm90IGludW5kYXRlZCkgYW5kIHZpY2UgdmVyc2VzLiAKCkZyb20gdGhlIHBsb3QsIHdlIGNhbiBvYnNlcnZlIHRoYXQgdGhlIEZhbHNlIE5lZ2F0aXZlIHJhdGUgaXMgcmF0aGVyIGhpZ2guIFRoaXMgaW5kaWNhdGVzIHRoYXQgdGhlcmUgYXJlIGNhc2VzIHdoZXJlIHRoZSBhY3R1YWwgY2xhc3MgaXMgYDFgIChpbnVuZGF0ZWQpLCBidXQgdGhlIG1vZGVsIGluY29ycmVjdGx5IGFzc2lnbnMgYSBwcmVkaWN0ZWQgY2xhc3Mgb2YgYDBgIChub3QgaW51bmRhdGVkKSBkdWUgdG8gdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBiZWluZyBiZWxvdyAwLjUuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgbW9kZWwgbWF5IG5vdCBiZSBhY2N1cmF0ZWx5IGlkZW50aWZ5aW5nIGFyZWFzIHRoYXQgYXJlIGxpa2VseSB0byBiZSBpbnVuZGF0ZWQuIAoKQWx0aG91Z2ggdGhlIEZhbHNlIE5lZ2F0aXZlIHJhdGUgaXMgaGlnaCwgdGhlIHBsb3QgcmV2ZWFscyBhIGhpZ2ggVHJ1ZSBQb3NpdGl2ZSByYXRlLiBUaGlzIG1lYW5zIHRoYXQgdGhlIG1vZGVsIGlzIGNvcnJlY3RseSBwcmVkaWN0aW5nIGFyZWFzIHRoYXQgYXJlIGxpa2VseSB0byBiZSBpbnVuZGF0ZWQsIHdpdGggYSBoaWdoIHByb2JhYmlsaXR5LiBUaGUgaGlnaCBUcnVlIFBvc2l0aXZlIHJhdGUgaXMgZW5jb3VyYWdpbmcsIGFzIGl0IGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCBoYXMgc29tZSBwcmVkaWN0aXZlIHBvd2VyIGFuZCBjYW4gYmUgdXNlZnVsIGluIGlkZW50aWZ5aW5nIGFyZWFzIHRoYXQgYXJlIHZ1bG5lcmFibGUgdG8gZmxvb2RpbmcuIEhvd2V2ZXIsIGl0IGlzIHN0aWxsIGltcG9ydGFudCB0byBhZGRyZXNzIHRoZSBGYWxzZSBOZWdhdGl2ZSByYXRlIHRvIGltcHJvdmUgdGhlIG1vZGVsJ3MgYWNjdXJhY3kgYW5kIHJlbGlhYmlsaXR5LgoKIyMgUk9DIGFuZCBBVUMgcGxvdHMKVGhlIHBsb3QgYmVsb3cgc2hvd3MgdGhlIFJPQyBjdXJ2ZSBhbmQgQVVDIGZvciB0aGUgbW9kZWwuIFRoZSBBVUMgdmFsdWUgb2YgMC45OSBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgaGFzIGEgdmVyeSBoaWdoIGRlZ3JlZSBvZiBhY2N1cmFjeSBpbiBwcmVkaWN0aW5nIHdoZXRoZXIgYW4gYXJlYSB3aWxsIGJlIGludW5kYXRlZCBvciBub3QuIEhvd2V2ZXIsIGl0IGhhcyBwb3RlbnRpYWwgdG8gYmUgb3ZlcmZpdHRpbmcgd2l0aCBuZXcgZGF0YXNldHMuCgpgYGB7ciByb2NfYXVjLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0Kcm9jX2RhdGEgPC0gZGF0YS5mcmFtZSgKICBEID0gYXMubG9naWNhbCh0ZXN0JGludW5kYXRlZCksCiAgTSA9IHRlc3QkcHJlZGljdGVkX3Byb2JzCikKCmdncGxvdChyb2NfZGF0YSwgYWVzKGQgPSBELCBtID0gTSkpICsKICBnZW9tX3JvYygpICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGxpbmV3aWR0aCA9IDEuNSwgY29sb3IgPSAiZ3JleSIpICsKICBsYWJzKAogICAgdGl0bGUgPSAiUk9DIEN1cnZlIiwKICAgIHN1YnRpdGxlID0gcGFzdGUoIkFyZWEgVW5kZXIgQ3VydmUgKEFVQyk6Iiwgcm91bmQoYXVjKHBST0M6OnJvYyh0ZXN0JGludW5kYXRlZCwgdGVzdCRwcmVkaWN0ZWRfcHJvYnMpKSwgNCkpLAogICAgeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIChGUFIpIiwKICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIChUUFIpIgogICkgCmBgYAoKIyBNYXAgcHJlZGljdGlvbgoKIyMgVHJhaW4gb24gZW50aXJlIENhbGdhcnkKVG8gZ2VuZXJhdGVkIHByZWRpY3RlZCBpbnVuZGF0aW9uLCB0aGUgZW50aXJlIENhbGdyYXkgZGF0YXNldGAgY2FsZ2FyeV9mb3JfbW9kZWxgIHdhcyB1c2VkIHRvIGNyZWF0ZSBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuIHdoZXJlIHRoZSB0YXJnZXQgdmFyaWFibGUgaXMgaW51bmRhdGVkLCBhbmQgYWxsIG90aGVyIHZhcmlhYmxlcyBhcmUgcHJlZGljdG9ycy4gQWZ0ZXIgdHJhaW5pbmcgdGhlIG1vZGVsLCB0aGUgYHByZWRpY3RgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gZ2VuZXJhdGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZm9yIHRoZSBlbnRpcmUgQ2FsZ2FyeSBkYXRhc2V0LiBUaGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgYXJlIHN0b3JlZCBpbiB0aGUgYGNhbGdhcnlfZm9yX21vZGVsJHByZWRpY3RlZF9wcm9ic2AgY29sdW1uLgoKYGBge3IgbW9kZWwyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbW9kZWwyIDwtIGdsbSgKICBpbnVuZGF0ZWQgfiAuLCBjYWxnYXJ5X2Zvcl9tb2RlbCAlPiUgZHBseXI6OnNlbGVjdCgtaWQpICU+JSBzdF9kcm9wX2dlb21ldHJ5KCksCiAgZmFtaWx5ID0gImJpbm9taWFsIihsaW5rID0gImxvZ2l0IikKKQoKc3VtbWFyeShtb2RlbDIpCgpjYWxnYXJ5X2Zvcl9tb2RlbCRwcmVkaWN0ZWRfcHJvYnMgPC0gcHJlZGljdChtb2RlbDIsIGNhbGdhcnlfZm9yX21vZGVsLCB0eXBlID0gInJlc3BvbnNlIikKYGBgCiMjIENvbmZ1c2lvbiBtYXRyaXggbWFwIGZvciBDYWxnYXJ5ClRvIG1hcCB0aGUgcHJlZGljdGVkIGludW5kYXRpb24gd2l0aCBlcnJvciBhbmQgYWNjdXJhY3ksIGEgY29uZnVzaW9uIG1hdHJpeCBtYXAgd2FzIHBsb3R0ZWQuIFRoZSBtdXRhdGUgZnVuY3Rpb24gaXMgdXNlZCB0byBjcmVhdGUgYSBuZXcgY29sdW1uIGNhbGxlZCBgY29uZlJlc3VsdGAsIHdoaWNoIGNhdGVnb3JpemVzIHRoZSBwcmVkaWN0ZWQgYW5kIGFjdHVhbCBjbGFzc2VzIGludG8gZm91ciBwb3NzaWJsZSBvdXRjb21lczogVHJ1ZSBOZWdhdGl2ZSwgVHJ1ZSBQb3NpdGl2ZSwgRmFsc2UgTmVnYXRpdmUsIGFuZCBGYWxzZSBQb3NpdGl2ZS4KCgpgYGB7ciBDb25mdXNpb25fbWF0cml4LCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KY2FsZ2FyeV9mb3JfbW9kZWwgJT4lCiAgbXV0YXRlKGNvbmZSZXN1bHQ9Y2FzZV93aGVuKHByZWRpY3RlZF9wcm9icyA8IDAuNSAmIGludW5kYXRlZD09MCB+ICJUcnVlX05lZ2F0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdGVkX3Byb2JzID49IDAuNSAmIGludW5kYXRlZD09MSB+ICJUcnVlX1Bvc2l0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdGVkX3Byb2JzIDwgMC41ICYgaW51bmRhdGVkPT0xIH4gIkZhbHNlX05lZ2F0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdGVkX3Byb2JzID49IDAuNSAmIGludW5kYXRlZD09MCB+ICJGYWxzZV9Qb3NpdGl2ZSIpKSAlPiUKICBnZ3Bsb3QoKSsKICBnZW9tX3NmKGFlcyhmaWxsID0gY29uZlJlc3VsdCksIGNvbG9yID0gInRyYW5zcGFyZW50IikrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiUmVkIiwiT3JhbmdlIiwiTGlnaHQgQmx1ZSIsIkxpZ2h0IEdyZWVuIiksCiAgICAgICAgICAgICAgICAgICAgbmFtZT0iT3V0Y29tZXMiKSsKICBsYWJzKHRpdGxlPSJDb25mdXNpb24gTWV0cmljcyIpICsKICBtYXBUaGVtZQpgYGAKCgojIyBQcmVkaWN0ZWQgaW51bmRhdGlvbiBtYXAgZm9yIENhbGdhcnkKCkJlbG93IGlzIHRoZSBwcmVkaWN0ZWQgaW51bmRhdGlvbiBtYXAgZm9yIENhbGdhcnkgdHJhaW5lZCB1c2luZyBlbnRpcmUgQ2FsZ2FyeSBkYXRhc2V0LgoKYGBge3IgaW51bmRhdGlvbl9wcmVkaWN0ZWQsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpnZ3Bsb3QoKSArCiAgZ2VvbV9zZihkYXRhPWNhbGdhcnlfZm9yX21vZGVsLCBhZXMoZmlsbD1wcmVkaWN0ZWRfcHJvYnMpKSArIAogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImJsdWUiLCBoaWdoID0gInJlZCIsIG5hbWUgPSAiUHJlZGljdGVkIHByb2JhYmlsaXR5IikgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBpbnVuZGF0aW9uIikgKyBtYXBUaGVtZQpgYGAKCiMjIFByZWRpY3RlZCBpbnVuZGF0aW9uIG1hcCBEZW52ZXIKClRoZW4gdGhlIGVudGlyZSBDYWxnYXJ5IHRyYWluZWQgdHJhaW5lZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHdhcyB1c2VkIHRvIHByZWRpY3QgaW51bmRhdGlvbiBwcm9iYWJpbGl0aWVzIGZvciB0aGUgRGVudmVyIGRhdGFzZXQuIFRoZSBwcmVkaWN0IGZ1bmN0aW9uIGlzIHVzZWQgdG8gZ2VuZXJhdGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZm9yIHRoZSBEZW52ZXIgZGF0YXNldCB1c2luZyB0aGUgbW9kZWwgb2JqZWN0IGNyZWF0ZWQgcHJldmlvdXNseS4gVGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIGFyZSB0aGVuIHN0b3JlZCBpbiB0aGUgYHByZWRpY3RlZF9wcm9ic2AgY29sdW1uIG9mIHRoZSBgZGVudmVyX3VzZWRgIGRhdGFzZXQgYW5kIG1hcHBlZCB1c2luZyBgZ2dwbG90YC4gVGhpcyBzdGVwIGFsbG93cyB1cyB0byBhcHBseSB0aGUgbW9kZWwgdG8gbmV3IGRhdGEgYW5kIGV2YWx1YXRlIGhvdyB3ZWxsIGl0IGdlbmVyYWxpemVzIHRvIG90aGVyIGFyZWFzLgoKVGhvdWdoIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmb3IgRGVudmVyIHdlcmUgY29tcGFyYXRpdmVseSBzbWFsbCwgdGhlIHByZWRpY3RlZCBjbGFzcyB3ZXJlIGRpc3Rpbmd1aXNoYWJsZSBpbiAKCgpgYGB7ciBpbnVuZGF0aW9uX2RlbnZlciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmRlbnZlcl91c2VkJHByZWRpY3RlZF9wcm9icyA8LSBwcmVkaWN0KG1vZGVsMiwgZGVudmVyX3VzZWQsIHR5cGUgPSAicmVzcG9uc2UiKQoKZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YT1kZW52ZXJfdXNlZCwgYWVzKGZpbGw9cHJlZGljdGVkX3Byb2JzKSkgKyAKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibHVlIiwgaGlnaCA9ICJyZWQiLG5hbWUgPSAiUHJlZGljdGVkIHByb2JhYmlsaXR5IikgKwogIGxhYnModGl0bGU9IlByZWRpY3RlZCBpbnVuZGF0aW9uIikgKyBtYXBUaGVtZQpgYGAKCgojIFN1bW1hcnkKVGhpcyBwcm9qZWN0IGFpbWVkIHRvIGVzdGltYXRlIGZsb29kIGludW5kYXRpb24gcHJvYmFiaWxpdGllcyB1c2luZyBhIHByZWRpY3RpdmUgbW9kZWwgZm9yIHRoZSBjaXRpZXMgb2YgQ2FsZ2FyeSBhbmQgRGVudmVyLiBVc2luZyBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscywgdGhlIHByZWRpY3Rpb24gcGVyZm9ybWFuY2Ugd2FzIGV2YWx1YXRlZCB1c2luZyBhIGNvbmZ1c2lvbiBtYXRyaXggYW5kIGFuIFJPQyBjdXJ2ZSwgd2hpY2ggaW5kaWNhdGVkIGFuIEFVQyBvZiAwLjk5IGFuZCBvdmVyYWxsIGdvb2QgcGVyZm9ybWFuY2UuCgpIb3dldmVyLCB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZm9yIERlbnZlciB3ZXJlIGNvbXBhcmF0aXZlbHkgc21hbGwsIGluZGljYXRpbmcgYSBsaW1pdGF0aW9uIGluIHRoZSBtb2RlbCdzIGFiaWxpdHkgdG8gZ2VuZXJhbGl6ZSB0byBuZXcgYXJlYXMuIEZ1cnRoZXJtb3JlLCB0aGUgQVVDIGlzIHVudXN1YWxseSBoaWdoIHdoaWNoIG1pZ2h0IGluZGljYXRlIHBvdGVudGlhbCBvdmVyZml0dGluZyBwcm9ibGVtcy4gRm9yIGZ1dHVyZSBpbXByb3ZlbWVudCwgb25lIHBvc3NpYmxlIGNvbnNpZGVyYXRpb24gaXMgdG8gdmFsaWRhdGUgdGhlIG1vZGVsIHVzaW5nIGFkZGl0aW9uYWwgY2l0aWVzLCB3aGljaCBjb3VsZCBtYWtlIHRoZSBtb2RlbCBtb3JlIHJpZ29yb3VzIGFuZCBhY2N1cmF0ZSBpbiBwcmVkaWN0aW5nIGZsb29kIGludW5kYXRpb24gcHJvYmFiaWxpdGllcyBmb3IgbmV3IGxvY2F0aW9ucy4=